#pragma once
#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include "log.hpp"
#include "socket.hpp"
#include "LockGuard.hpp"
#include "InetAddr.hpp"
using namespace socket_ns;
class epoll_server
{
    const static int gnum = 64;

public:
    epoll_server(uint16_t port = 8888)
        : _port(port),
          _listen_sock(std::make_unique<tcp_socket>()),
          _timeout(-1),
          _epfd(-1)
    {
        // 1.创建listensock
        InetAddr addr("0", _port);
        _listen_sock->build_server_socket(addr);
        // 2. 创建epoll模型
        _epfd = ::epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll creat error!\n");
            exit(5);
        }
        LOG(DEBUG, "epoll_creat success epfd : %d\n", _epfd);
        struct epoll_event event;
        event.data.fd = _listen_sock->SockFd();
        event.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_sock->SockFd(), &event);
        LOG(DEBUG, "add listen socket fd is: %d\n", _listen_sock->SockFd());
    }
    ~epoll_server()
    {
        _listen_sock->close_sockfd();
        if (_epfd > 0)
        {
            ::close(_epfd);
        }
    }
    void accept_client()
    {
        InetAddr client_addr;
        int sockfd = _listen_sock->accepter(&client_addr); // 此时不会阻塞，因为已经有客户端发起连接请求了
        if (sockfd < 0)
        {
            return;
        }
        LOG(DEBUG, "Get new Link, sockfd: %d, client info %s:%d\n", sockfd, client_addr.Ip().c_str(), client_addr.Port());
        // 获取新链接成功
        // 读取或者写入呢？不能！谁知道newfd上面有对应的事件就绪了呢？epoll
        struct epoll_event event;
        event.data.fd = sockfd;
        event.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &event);
        // int pos = 1;
        // // 从小的位置开始放文件描述符
        // for (; pos < gnum; pos++)
        // {
        //     if (_revents[pos].data.fd == gdefaultfd)
        //         break;
        // }
        // if (pos == gnum)
        // {
        //     ::close(sockfd);
        //     LOG(WARNING, "server is full! \n");
        //     return;
        // }
        // else
        // {
        //     _events[pos].fd = sockfd;
        //     _events[pos].events = POLLIN;
        //     _events[pos].revents = gdefaultevent;
        //     LOG(DEBUG, "%d add to select array!\n", sockfd);
        // }
        LOG(DEBUG, "_listensock ready, accept done ,epoll_ctl done ,newfd is: %d\n", sockfd);
    }
    void service_io(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(_revents[pos].data.fd, buffer, sizeof(buffer) - 1, 0); // 读取文件描述符内容，不会阻塞，文件内有内容了
        if (n > 0)                                                          // 读取成功
        {
            buffer[n] = 0;
            std::cout << "client say:" << buffer << std::endl;
            std::string echo_string = "[sercer echo]# ";
            echo_string += buffer;
            ::send(_revents[pos].data.fd, echo_string.c_str(), echo_string.size(), 0);
        }
        else if (n == 0) // 对面关闭了连接
        {
            ::epoll_ctl(_epfd,EPOLL_CTL_DEL,_revents[pos].data.fd,nullptr);//要删除的fd必须是合法的
            ::close(_revents[pos].data.fd);

            LOG(DEBUG, "normal fd %d close, me too!\n", _revents[pos].data.fd);
        }
        else // 错误
        {
            ::epoll_ctl(_epfd,EPOLL_CTL_DEL,_revents[pos].data.fd,nullptr);
            ::close(_revents[pos].data.fd);
        }
    }


    
    void handler_event(int num)
    {
        for (int i = 0; i < num; i++)
        {
            int fd = _revents[i].data.fd;
            short revents = _revents[i].events;
            // 多种情况会导致socket 读事件就绪：需要辨别
            if (revents & EPOLLIN)
            {
                if (fd == _listen_sock->SockFd()) // 等于监听套接字时，此时有客户端发起连接请求
                {
                    accept_client();
                }
                else //_fd_array[i]文件描述符中可以读取该文件描述符了
                {
                    service_io(i);
                }
            }
            else
            {
                // TODO
            }
        }
    }
    void loop()
    {
        int timeout = -1;
        // struct timeval
        // {
        //     long tv_sec;  // 秒数
        //     long tv_usec; // 微秒数
        // };
        while (true)
        {
            int n = ::epoll_wait(_epfd, _revents, gnum, _timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "poll timeout...\n");
                break;
            case -1:
                LOG(ERROR, "select error...\n");
                break;
            default:
                LOG(DEBUG, "Event Happen. n : %d\n", n);
                handler_event(n); // 处理事件
                break;
            }
        }
    }

private:
    uint16_t _port;                         // 端口号
    std::unique_ptr<mysocket> _listen_sock; // sock指针
    int _epfd;
    int _timeout;
    struct epoll_event _revents[gnum];
};